package com.essence.grabiec104.grab.executor;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.*;

/**
 * @brief 多线程执行器
 * @details 线程池管理器，用于管理线程池，在程序关闭时自动关闭线程池
 */
public class SaveExecutor {
    private static Logger LOG = LoggerFactory.getLogger(SaveExecutor.class);
    private static final Map<String, ExecutorService> pools = new ConcurrentHashMap<>();

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            for (ExecutorService pool : pools.values()) {
                pool.shutdown();
                try {
                    if (!pool.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
                        pool.shutdownNow();
                    }
                } catch (InterruptedException ignored) {
                }
            }
        }));
    }

    public static ExecutorService addPool(String name, int size) {
        ThreadFactory nameThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat(name + "-%d")
                .setDaemon(false)
                .setUncaughtExceptionHandler((t, e) -> LOG.error("线程池:{} 线程:{} 异常:{}", name, t.getName(), e.getMessage()))
                .build();

        ThreadPoolExecutor executor = new ThreadPoolExecutor(size, size, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<>(),
                nameThreadFactory);
        pools.put(name, executor);
        return executor;
    }


    /**
     * @param callable 需要包装的Callable
     * @param cleanup  清理工作
     * @brief 包装Callable
     * @details 包装一个Callable，在finally中执行清理工作, 包装后的Callable可以作为ThreadPool的submit()参数
     */
    public static <T> Callable<T> wrap(Callable<T> callable, Runnable cleanup) {
        return () -> {
            try {
                return callable.call();
            } finally {
                cleanup.run();
            }
        };
    }

    /**
     * @brief 包装Runnable
     * @details 包装一个Runnable
     * @see #wrap(Callable, Runnable)
     * @param runnable 需要包装的Runnable
     * @param cleanup  清理工作
     */
    public static Runnable wrap(Runnable runnable, Runnable cleanup) {
        return () -> {
            try {
                runnable.run();
            } finally {
                cleanup.run();
            }
        };
    }
}
